package com.ruoyi.client.fxiaoke.service.impl;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;

import com.ruoyi.client.fxiaoke.beans.args.CrmDeleteArg;
import com.ruoyi.client.fxiaoke.beans.args.CrmQueryV2Arg;
import com.ruoyi.client.fxiaoke.beans.args.CrmUpdateArg;
import com.ruoyi.client.fxiaoke.beans.results.BaseResult;
import com.ruoyi.client.fxiaoke.beans.results.CrmQueryV2Result;
import com.ruoyi.client.fxiaoke.beans.results.CrmResult;
import com.ruoyi.client.fxiaoke.service.CRMService;
import com.ruoyi.client.fxiaoke.utils.OpenAPIUtils;
import com.ruoyi.client.fxiaoke.utils.TokenUtil;
import com.ruoyi.core.api.util.PropertyUtil;
import com.ruoyi.core.util.BeanUtils;
import com.ruoyi.core.util.Dom4jUtil;
import com.ruoyi.platform.model.system.FXiaoKeParameter;


@Service
public class CRMServiceImpl implements CRMService {
	private static final Log logger = LogFactory.getLog(Dom4jUtil.class);
	private static Map<String,String> apiMap = new HashMap<>();
	private static final Map<String,Map<String,Object>> apiModel = new ConcurrentHashMap<>();

	@Override
	public List<Map<String, Object>> getDataAll(String apiName) throws Exception {
		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName);
		TokenUtil.setCorpAccessToken(arg);
		return getData(arg);
	}

	@Override
	public List<Map<String, Object>> getData(CrmQueryV2Arg arg) throws Exception {
		return getData(arg,0D);
	}

	public List<Map<String, Object>> getData(CrmQueryV2Arg arg,Double offset) throws Exception {

		//设置API对象
		String apiName = arg.getData().getDataObjectApiName();
		arg.getData().setDefineType(getApiObjcet(apiName));

		//field_9sh11__c
//				if(arg.getData().getSearch_query_info().getFilters().size() == 0) {
//					List<CrmQueryV2Arg.Filter> filters = new ArrayList<>();
//					CrmQueryV2Arg.Filter filter = new CrmQueryV2Arg.Filter();
//					filter.setField_name("field_Gq62v__c");
//					List<String> values = new ArrayList<>();
//					values.add("0");
//					filter.setField_values(values);
//					filter.setOperator("EQ");
//					filters.add(filter);
//					
//					arg.getData().getSearch_query_info().setFilters(filters);
//				}

//		if(arg.getData().getSearch_query_info().getFilters().size() == 0) {
//			List<CrmQueryV2Arg.Filter> filters = new ArrayList<>();
//			CrmQueryV2Arg.Filter filter = new CrmQueryV2Arg.Filter();
//			filter.setField_name("account_no");
//			List<String> values = new ArrayList<>();
//			values.add("C2020070100017");
//			filter.setField_values(values);
//			filter.setOperator("IN");
//			filters.add(filter);
//			arg.getData().getSearch_query_info().setFilters(filters);
//		}


		logger.debug(arg);
		CrmQueryV2Result crm = OpenAPIUtils.queryCrmData(arg);
		if(crm.getErrorCode() == 20016) {
			TokenUtil.resetCorpAccessToken();
			TokenUtil.setCorpAccessToken(arg);
			getData(arg);
		}
		logger.debug(crm);
		if(crm.getErrorCode() != 0) {
			
			String msg = crm.getErrorMessage();
			if(msg.contains("shsw168 call model crm.customobject.query times is out of limit 80 every 20 seconds!")) {
				Thread.sleep(3000L);
				return getData(arg,offset);
			}
					
			throw new Exception(crm.getErrorMessage());
		}
		if(!"success".equals(crm.getErrorDescription()) && crm.getErrorDescription() != null) {
			String msg = crm.getErrorDescription();
			if(msg.contains("shsw168 call model crm.customobject.query times is out of limit 80 every 20 seconds!")) {
				Thread.sleep(3000L);
				return getData(arg,offset);
			}
			
			throw new Exception(crm.getErrorDescription());
		}
			

		Map<String,Object> data = crm.getData();
		offset =  data.containsKey("offset")?(Double) data.get("offset"):offset;
		Double total = (Double) data.get("total");
		
		

		//递归查询所有数据
		@SuppressWarnings("unchecked")
		List<Map<String,Object>> dataList = (List<Map<String,Object>>)data.get("dataList");
		if(offset > 1000)return dataList;
		if(total > offset) {
			offset +=  100;
			arg.getData().getSearch_query_info().setOffset(offset.intValue());
			dataList.addAll(getData(arg,offset));
		}

		return dataList;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Map<String, Object>> getObjectList() throws Exception {
		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID));
		TokenUtil.setCorpAccessToken(arg);

		//获取接口列表
		CrmQueryV2Result result = OpenAPIUtils.getCrmList(arg);

		if(result.getErrorCode() != 0)
			throw new Exception(result.getErrorMessage());

		Map<String,Object> map = result.getData();

		List<Map<String,Object>> dataList = (List<Map<String,Object>>)map.get("objects");

		//放入全局缓存
		for (Map<String, Object> objMap : dataList) {
			String apiName = objMap.getOrDefault("describeApiName", "").toString();
			String type = objMap.getOrDefault("defineType", "").toString();
			apiMap.put(apiName, "package".equals(type)?"v2":type);
			apiModel.put(apiName, objMap);
		}

		return dataList;
	}

	@Override
	public Map<String, Object> getById(String apiName, String dataId) throws Exception {

		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName);
		TokenUtil.setCorpAccessToken(arg);
		arg.getData().setObjectDataId(dataId);
		arg.getData().setDefineType(getApiObjcet(apiName));

		CrmQueryV2Result crm = OpenAPIUtils.getData(arg);
		if(crm.getErrorCode() == 20016) {
			TokenUtil.resetCorpAccessToken();
			getById(apiName,dataId);
		}
		if(crm.getErrorCode() == 320002500) {
			logger.error(crm);
			return null;
		}
		
		if(crm.getErrorCode() != 0) {
			logger.error(crm);
			String msg = crm.getErrorMessage();
			if(msg.contains("shsw168 call model crm.customobject.query times is out of limit 80 every 20 seconds!")) {
				Thread.sleep(3000L);
				return getById(apiName,dataId);
			}
			throw new Exception(crm.getErrorMessage());
		}
		if(!"OK".equals(crm.getErrorMessage()) && !"success".equals(crm.getErrorDescription())) {
			logger.error(crm);
			String msg = crm.getErrorDescription();
			if(msg.contains("shsw168 call model crm.customobject.query times is out of limit 80 every 20 seconds!")) {
				Thread.sleep(3000L);
				return getById(apiName,dataId);
			}
			throw new Exception(crm.getErrorDescription());
		}
		return crm.getData();
	}


	/**
	 * 获取获取接口对象   自定义对象/预设对象
	 * @param apiName
	 * @return
	 * @throws Exception 
	 */
	private String getApiObjcet(String apiName) throws Exception {
		//获取API对象
		if(!apiMap.containsKey(apiName)) {
			getObjectList();
		}
		return apiMap.get(apiName);
	}

	@Override
	public List<Map<String, Object>> getByRefId(String apiName,String pkName, String dataId) throws Exception {
		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName);

		List<CrmQueryV2Arg.Filter> filters = new ArrayList<>();

		CrmQueryV2Arg.Filter filter = new CrmQueryV2Arg.Filter();
		filter.setField_name(pkName);
		List<Object> values = new ArrayList<>();
		values.add(dataId);
		filter.setField_values(values);
		filter.setOperator("EQ");
		filters.add(filter);

		arg.getData().getSearch_query_info().setFilters(filters);
		TokenUtil.setCorpAccessToken(arg);
		return getData(arg);
	}

	@Override
	public Map<String, Object> getApiModel(String apiName) {
		if(apiModel.containsKey(apiName))
			return apiModel.get(apiName);
		try {
			getObjectList();
		} catch (Exception e) {
		}


		return apiModel.get(apiName);
	}

	@Override
	public void update(CrmUpdateArg arg) throws Exception {
		TokenUtil.setCorpAccessToken(arg);
		arg.setCurrentOpenUserId(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID));
		arg.setDefineType(getApiObjcet(arg.getApiName()));
		CrmResult result = OpenAPIUtils.updateCrmData(arg);
		logger.debug(arg);
		if(result.getErrorCode() != 0)
			throw new Exception(result.getErrorMessage());
	}

	@Override
	public String create(CrmUpdateArg arg) throws Exception {
		TokenUtil.setCorpAccessToken(arg);
		arg.setCurrentOpenUserId(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID));
		arg.setDefineType(getApiObjcet(arg.getApiName()));
		logger.debug(arg);
		CrmResult result = OpenAPIUtils.createCrmData(arg);
		if(result.getErrorCode() != 0)
			throw new Exception(result.getErrorMessage());
		return result.getDataId();
	}
	
	/**
	 * 同步数据
	 */
	@Override
	public CrmResult save(CrmUpdateArg arg) throws Exception {
		TokenUtil.setCorpAccessToken(arg);
		arg.setCurrentOpenUserId(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID));
		arg.setDefineType(getApiObjcet(arg.getApiName()));
		CrmResult result = null;
		logger.debug(arg);
		if(arg.getObject_data().containsKey("_id")) {
			 result = OpenAPIUtils.updateCrmData(arg);
		}else {
			result = OpenAPIUtils.createCrmData(arg);
		}
		if(result.getErrorCode() != 0)
			throw new Exception(result.getErrorMessage());
		
		return result;
	}

	/**
	 * 根据编码查询数据
	 */
	@Override
	public Map<String, Object> getByCode(String apiName, String codeField, String code) throws Exception {
		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName);
		TokenUtil.setCorpAccessToken(arg);
		arg.addFilter(codeField, code, "EQ");
		List<Map<String,Object>> dataList = this.getData(arg);
		if(dataList.size() == 0)
			return null;
		return dataList.get(0);
	}
	
	/**
	 * 查询接口
	 * @param apiName		接口名称
	 * @param param		查询参数
	 * @return
	 * @throws Exception 
	 */
	@Override
	public List<Map<String, Object>> query(String apiName, Map<String, Object> param) throws Exception {
		CrmQueryV2Arg arg = new CrmQueryV2Arg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName);
		TokenUtil.setCorpAccessToken(arg);
		for (Entry<String, Object> entry : param.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue().toString();
			arg.addFilter(key, value, "EQ");
		}
		List<Map<String,Object>> dataList = this.getData(arg);
		return dataList;
	}

	/**
	 * 添加CRM数据
	 * @param apiName		API对象名称
	 * @param dataMap		对象数据
	 * @return	单据主键
	 * @throws Exception
	 */
	@Override
	public String save(String apiName, Map<String, Object> dataMap) throws Exception {
		CrmUpdateArg arg = new CrmUpdateArg(apiName);
		arg.setObject_data(dataMap);
		Object _id = dataMap.get("_id");
		if(BeanUtils.isNotEmpty(_id)) {
			this.update(arg);
			return _id.toString();
		}else {
			return this.create(arg);
		}
	}

	@Override
	public BaseResult invalidById(String apiName, Object dataId) throws Exception {
		CrmDeleteArg arg = new CrmDeleteArg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName,dataId.toString());
		TokenUtil.setCorpAccessToken(arg);
		BaseResult result = OpenAPIUtils.dropCrmData(arg);
		return result;
	}

	@Override
	public BaseResult deleteById(String apiName, Object dataId) throws Exception {
		CrmDeleteArg arg = new CrmDeleteArg(PropertyUtil.getByAlias(FXiaoKeParameter.FXIAOKE_OPENUSERID),apiName,dataId.toString());
		TokenUtil.setCorpAccessToken(arg);
		BaseResult result = OpenAPIUtils.deleteCrmData(arg);
		return result;
	}

}
